﻿using System;

namespace BaseApplicationNamespace
{
    /// <summary>
    /// アプリケーションの一つの操作を実行する操作スクリプトの基本クラスです。
    /// </summary>
    /// <remarks>
    /// <para>
    /// <see cref="OperationScript&lt;TOutputs, TErrors&gt;"/> クラスは、操作スクリプトパターンをコマンドパターンで実現します。
    /// </para>
    /// <para>
    /// <see cref="OperationScript&lt;TOutputs, TErrors&gt;.Execute()"/> メソッドを呼び出すことで操作が実行されます。
    /// </para>
    /// <para>
    /// 操作が成功した場合、<see cref="OperationScript&lt;TOutputs, TErrors&gt;.Success"/> イベントが発生します。
    /// 出力は <see cref="SuccessEventArgs&lt;TOutputs&gt;"/> の <see cref="SuccessEventArgs&lt;TOutput&gt;.Outputs"/> プロパティから取得できます。
    /// </para>
    /// <para>
    /// 操作が失敗した場合、<see cref="OperationScript&lt;TOutputs, TErrors&gt;.Error"/> イベントが発生します。
    /// エラーは <see cref="ErrorEventArgs&lt;TOutputs&gt;"/> の <see cref="ErrorEventArgs&lt;TOutput&gt;.Errors"/> プロパティから取得できます。
    /// エラーは列挙型ではなく <see cref="OperationErrors"/> クラスの派生クラスとして表されます。
    /// </para>
    /// <para>
    /// アプリケーションの動作として想定されない状況については 
    /// <see cref="OperationScript&lt;TOutputs, TErrors&gt;.Error"/> イベントは発生せず、
    /// <see cref="OperationScript&lt;TOutputs, TErrors&gt;.Execute()"/> メソッドの実行時に例外がスローされます。
    /// </para>
    /// </remarks>
    /// <typeparam name="TOutputs">出力の型。</typeparam>
    /// <typeparam name="TErrors">エラーの型。</typeparam>
    /// <seealso cref="OperationOutputs"/>
    /// <seealso cref="OperationErrors"/>
    /// <seealso cref="OperationScript&lt;TOutputs, TErrors&gt;.ExecutionResult"/>
    [Serializable]
    public abstract partial class OperationScript<TOutputs, TErrors>
        where TOutputs : OperationOutputs
        where TErrors : OperationErrors
    {
        /// <summary>
        /// 操作が成功した場合に発生します。
        /// </summary>
        public event SuccessEventHandler<TOutputs> Success;

        /// <summary>
        /// 操作が失敗した場合に発生します。
        /// </summary>
        public event ErrorEventHandler<TErrors> Error;


        /// <summary>
        /// 操作を実行します。
        /// </summary>
        /// <exception cref="System.InvalidOperationException">操作の実行に失敗しました。</exception>
        public void Execute()
        {
            var result = ExecuteCore();
            if (result.IsSuccess && (Success != null))
            {
                Success(this, new SuccessEventArgs<TOutputs>(result.Outputs));
            }
            if (result.IsError && (Error != null))
            {
                Error(this, new ErrorEventArgs<TErrors>(result.Errors));
            }
        }

        /// <summary>
        /// 操作を実行します。
        /// </summary>
        /// <returns>
        /// <para>
        /// 実行結果。
        /// </para>
        /// <para>
        /// 操作が成功した場合は <typeparamref name="TOutputs"/> のインスタンスを元に生成された <see cref="ExecutionResult"/>、
        /// 操作が失敗した場合は <typeparamref name="TErrors"/> のインスタンスを元に生成された <see cref="ExecutionResult"/> を返します。
        /// </para>
        /// </returns>
        /// <exception cref="System.InvalidOperationException">操作の実行に失敗しました。</exception>
        protected abstract ExecutionResult ExecuteCore();
    }
}
